/* ----------------------------------------------------------------------------------------------------------------
 *  InputManager
 * ----------------------------------------------------------------------------------------------------------------
 */
package pure.engine.core
{
	import flash.events.KeyboardEvent;

	use namespace ns_despair;
	
	/**
	 * 按键管理器
	 * [Author] - PURE
	 * 
	 * [Feature] - [-1:justRelease]，[0:None]，[1:Press]，[2:justPress]
	 */
final public class InputManager implements IFrameListener
{
	public function InputManager() 
	{
		_map = new Vector.<keyObject>(_total, true);
		_addAllKeys();
		
		SystemManager.m_stage.addEventListener(KeyboardEvent.KEY_DOWN, ____onKeyDown);
		SystemManager.m_stage.addEventListener(KeyboardEvent.KEY_UP,   ____onKeyUp);
		
		ProcessManager.instance.addFrameListener(this, ProcessManager.INPUT);
	}

	
	
	/**
	 * 是否按下某键
	 * 
	 * [Param] - key  键名
	 */
	final public function pressed(key:String):Boolean
	{
		return this[key]; 
	}

	
	/**
	 * 是否刚按下某键
	 * 
	 * [Param] - key  键名
	 */
	final public function justPressed(key:String):Boolean 
	{ 
		return _map[m_lookup[key]].current == 2;
	}

	
	/**
	 * 是否刚弹起某键
	 * 
	 * [Param] - key  键名
	 */
	final public function justReleased(key:String):Boolean 
	{ 
		return _map[m_lookup[key]].current == -1;
	}

	
	/**
	 * 是否有按下任何键
	 */
	final public function any():Boolean
	{
		var K:keyObject;
		var i:uint = 0;
		
		while (i < _total) 
		{
			K = _map[i++];
			if (K && K.current > 0)
			{
				return true;
			}
		}
		return false;
	}
	
	
	/**
	 * 重置
	 */
	final public function reset():void
	{
		var K:keyObject;
		var i:uint = 0;
		
		while (i < _total) 
		{
			K = _map[i++];
			if(!K) continue;
			this[K.name] = false;
			K.current = K.last = 0;
		}
	}

	/**
	 * If any keys are not "released" (0),
	 * this function will return an array indicating
	 * which keys are pressed and what state they are in.
	 * 
	 * @return	An array of key state data.  Null if there is no data.
	 */
	public function record():Array
	{
		var data:Array = null;
		var i:uint = 0;
		while(i < _total)
		{
			var o:Object = _map[i++];
			if((o == null) || (o.current == 0))
				continue;
			if(data == null)
				data = new Array();
			data.push({code:i-1,value:o.current});
		}
		return data;
	}
	
	/**
	 * Part of the keystroke recording system.
	 * Takes data about key presses and sets it into array.
	 * 
	 * @param	Record	Array of data about key states.
	 */
	public function playback(Record:Array):void
	{
		var i:uint = 0;
		var l:uint = Record.length;
		var o:Object;
		var o2:Object;
		while(i < l)
		{
			o = Record[i++];
			o2 = _map[o.code];
			o2.current = o.value;
			if (o.value > 0)
			{
				this[o2.name] = true;
			}
		}
	}
	
	
	/**
	 * 帧更新
	 * 
	 * [Process] - 1. (本帧)更新上一帧按键状态
	 *           - 2. (次帧)根据上一帧按键状态，判断当前按键状态
	 * 
	 * [Hint] - [-1] justRelease，[0] Release，[1] Press，[2] justPress
	 */
	public function update(deltaTime:int):void
	{
		var i:uint = 0;
		while (i < _total) 
		{
			var K:keyObject = _map[i++];
			
			if (!K) continue;
			
			// justRelease
			if (K.last == -1 && K.current == -1)
			{
				K.current = 0;
			}
			
			// justPress
			else if (K.last == 2 && K.current == 2)
			{
				K.current = 1;
			}
			
			// 更新上一帧按键状态
			K.last = K.current;
		}
	}
	

	
	
	/* ----------------------------------------------------------------------------------------------------------------
	 * ★★★★★★★★★★★★★★★★★★★★★  private  ★★★★★★★★★★★★★★★★★★★★★
	 * ----------------------------------------------------------------------------------------------------------------
	 */
	


	/**
	 * 加入全部按键
	 */
	private function _addAllKeys():void 
	{
		// 字母A-Z
		var i:uint = 65;
		while (i <= 90) 
		{
			_addKey(String.fromCharCode(i),i++);
		}
		
		// 数字
		i = 48;
		_addKey("ZERO",i++);
		_addKey("ONE",i++);
		_addKey("TWO",i++);
		_addKey("THREE",i++);
		_addKey("FOUR",i++);
		_addKey("FIVE",i++);
		_addKey("SIX",i++);
		_addKey("SEVEN",i++);
		_addKey("EIGHT",i++);
		_addKey("NINE", i++);
		
		//i = 96;
		//_addKey("NUMPADZERO",i++);
		//_addKey("NUMPADONE",i++);
		//_addKey("NUMPADTWO",i++);
		//_addKey("NUMPADTHREE",i++);
		//_addKey("NUMPADFOUR",i++);
		//_addKey("NUMPADFIVE",i++);
		//_addKey("NUMPADSIX",i++);
		//_addKey("NUMPADSEVEN",i++);
		//_addKey("NUMPADEIGHT",i++);
		//_addKey("NUMPADNINE",i++);
		//_addKey("PAGEUP", 33);
		//_addKey("PAGEDOWN", 34);
		//_addKey("HOME", 36);
		//_addKey("END", 35);
		//_addKey("INSERT", 45);
		
		// F1~F12
		//i = 1;
		//while(i <= 12) _addKey("F"+i,111+(i++));
		
		//SPECIAL KEYS + PUNCTUATION
		_addKey("ESC",27);
		//_addKey("MINUS",189);
		//_addKey("PLUS",187);
		//_addKey("DELETE",46);
		//_addKey("BACKSPACE",8);
		//_addKey("LBRACKET",219);
		//_addKey("RBRACKET",221);
		//_addKey("BACKSLASH",220);
		//_addKey("CAPSLOCK",20);
		//_addKey("SEMICOLON",186);
		//_addKey("QUOTE",222);
		//_addKey("ENTER",13);
		//_addKey("SHIFT",16);
		//_addKey("COMMA",188);
		//_addKey("PERIOD",190);
		//_addKey("SLASH",191);
		_addKey("CONTROL",17);
		_addKey("ALT",18);
		_addKey("SPACE",32);
		_addKey("UP",38);
		_addKey("DOWN",40);
		_addKey("LEFT",37);
		_addKey("RIGHT",39);
		//_addKey("TAB", 9);
	}
	
	
	/**
	 * 加入按键
	 * 
	 * [Param] - keyName
	 * [Param] - keyCode
	 */
	private function _addKey(keyName:String, keyCode:uint):void
	{
		m_lookup[keyName]  =  keyCode;
		_map[keyCode]      =  new keyObject(keyName);
	}

	
	
	
	
	
	//======================
	// Event
	//======================
	
	
	/**
	 * [Event]
	 * 键下
	 * 
	 * [Hint] - [-1] justRelease，[0] Release，[1] Press，[2] justPress
	 */
	private function ____onKeyDown(e:KeyboardEvent):void
	{
		e.updateAfterEvent();
		
		var K:keyObject = _map[e.keyCode];
		
		if (!K) return;
		
		//K.current = Boolean(K.current > 0) ? 1 : 2;
		K.current = 2;
		this[K.name] = true;
	}

	
	/**
	 * [Event]
	 * 弹起
	 */
	private function ____onKeyUp(e:KeyboardEvent):void 
	{
		e.updateAfterEvent();
		
		var K:keyObject = _map[e.keyCode];
		
		if (!K) return;
		
		//K.current = Boolean(K.current > 0) ? -1 : 0;
		K.current = -1;
		this[K.name] = false;
	}

	
	
	
	
	//======================
	// Member
	//======================
	
	
	private var m_lookup:Object = { };  // keyName : keyCode
	
	private var _map:Vector.<keyObject>;
	
	private const _total:uint = 91;  // 256
	
	
	public var ESC:Boolean, ENTER:Boolean, SHIFT:Boolean, CONTROL:Boolean, ALT:Boolean, SPACE:Boolean, UP:Boolean, DOWN:Boolean, LEFT:Boolean, RIGHT:Boolean;

	public var Q:Boolean, W:Boolean, E:Boolean, R:Boolean, T:Boolean, Y:Boolean, U:Boolean, I:Boolean, O:Boolean, P:Boolean, A:Boolean, S:Boolean, D:Boolean, F:Boolean, G:Boolean, H:Boolean, J:Boolean, K:Boolean, L:Boolean, Z:Boolean, X:Boolean, C:Boolean, V:Boolean, B:Boolean, N:Boolean, M:Boolean;

	public var ONE:Boolean, TWO:Boolean, THREE:Boolean, FOUR:Boolean, FIVE:Boolean, SIX:Boolean, SEVEN:Boolean, EIGHT:Boolean, NINE:Boolean, ZERO:Boolean;
	
	public var F1:Boolean, F2:Boolean, F3:Boolean, F4:Boolean, F5:Boolean, F6:Boolean, F7:Boolean, F8:Boolean, F9:Boolean, F10:Boolean, F11:Boolean, F12:Boolean;
	
	public var NUMPADONE:Boolean, NUMPADTWO:Boolean, NUMPADTHREE:Boolean, NUMPADFOUR:Boolean, NUMPADFIVE:Boolean, NUMPADSIX:Boolean, NUMPADSEVEN:Boolean, NUMPADEIGHT:Boolean, NUMPADNINE:Boolean, NUMPADZERO:Boolean;

	public var PAGEUP:Boolean, PAGEDOWN:Boolean, HOME:Boolean, END:Boolean, INSERT:Boolean, MINUS:Boolean, PLUS:Boolean, DELETE:Boolean, BACKSPACE:Boolean, TAB:Boolean, CAPSLOCK:Boolean;
	
	//public var LBRACKET:Boolean;    // [
	//public var RBRACKET:Boolean;    // ]
	//public var BACKSLASH:Boolean;    // "\"
	//public var SEMICOLON:Boolean;    // ;
	//public var QUOTE:Boolean;    // ""
	//public var COMMA:Boolean;    // ,
	//public var PERIOD:Boolean;    // .
	//public var SLASH:Boolean;    // /

}
}

final class keyObject
{
	public function keyObject(keyName:String)
	{
		this.name = keyName;
	}
	
	
	public var name:String;
	
	public var current:int, last:int;
}
